1 module hip.util.conv;
2 import hip.util.string;
3 import hip.util.reflection:isArray;
4 public import hip.util.to_string_range;
5 public import hip.util.string:toStringz;
6 
7 
8 string toString(dstring dstr) pure nothrow @safe
9 {
10     try
11     {
12         string ret;
13         foreach(ch; dstr)
14             ret~= ch;
15         return ret;
16     }
17     catch(Exception e){return "";}
18 }
19 
20 string toString(char[] arr) pure nothrow @trusted @nogc {return cast(string)arr;}
21 string toString(T)(T[] arr) pure nothrow @safe if(!is(T == char))
22 {
23     string ret = "[";
24     for(int i = 0; i < arr.length; i++)
25     {
26         if(i)
27             ret~= ",";
28         ret~= toString(arr[i]);
29     }
30     return ret~"]";
31 }
32 
33 
34 string toString(T)(T structOrTupleOrEnum) pure nothrow @safe if(!isArray!T)
35 {
36     static if(is(T == struct))//For structs declaration
37     {
38         import hip.util.reflection;
39         static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format"))
40         {
41             import hip.util.format;
42             return formatFromType(structOrTupleOrEnum);
43         }
44         else
45         {
46             alias struct_ = structOrTupleOrEnum;
47             string s = "(";
48             static foreach(i, alias m; T.tupleof)
49             {
50                 if(i > 0)
51                     s~= ", ";
52                 s~= to!string(struct_.tupleof[i]);
53             }
54             return T.stringof~s~")";
55         }
56     }
57     else static if(is(T == enum))
58     {
59         foreach(mem; __traits(allMembers, T))
60             if(__traits(getMember, T, mem) == structOrTupleOrEnum)
61                 return T.stringof~"."~mem;
62         return T.stringof~".|MEMBER_NOT_FOUND|";
63     }
64     else static if(__traits(isAssociativeArray, T))
65     {
66         string s;
67         try
68         {
69             foreach(key, value; structOrTupleOrEnum)
70             {
71                 s~= "\n\t"~key.to!string~": "~value.to!string;
72             }
73         }
74         catch(Exception e){}
75         return "["~s~"\n]";
76     }
77     // else static if(isD)
78     else static assert(0, "Not implemented for "~T.stringof);
79     // static if(isTuple!T)
80     // {
81     //     alias tupl = structOrTupleOrEnum;
82     //     string ret;
83     //     foreach(i, v; tupl)
84     //     {
85     //         if(i > 0)
86     //             ret~= ", ";
87     //         ret~= to!string(v);
88     //     }
89     //     return T.stringof~"("~ret~")";
90     // }
91     // else
92 }
93 
94 
95 
96 string dumpStructToString(T)(T struc) if(is(T == struct))
97 {
98     string s = "\n(";
99     static foreach(i, alias m; T.tupleof)
100     {
101         s~= "\n\t "~m.stringof~": ";
102         s~= toString(struc.tupleof[i]);
103     }
104     return T.stringof~s~"\n)";
105 }
106 
107 
108 T toStruct(T)(string struc) pure nothrow
109 {
110     T ret;
111     string[] each;
112     string currentArg;
113 
114     bool isInsideString = false;
115     for(size_t i = 1; i < (cast(int)struc.length)-1; i++)
116     {
117         if(!isInsideString && struc[i] == ',')
118         {
119             each~= currentArg;
120             currentArg = null;
121             if(struc[i+1] == ' ')
122             	i++;
123             continue;
124         }
125         else if(struc[i] == '"')
126         {
127             isInsideString = !isInsideString;
128             continue;
129         }
130         currentArg~= struc[i];
131     }
132     if(currentArg.length != 0)
133         each~=currentArg;
134 
135     static foreach(i, m; __traits(allMembers, T))
136     {{
137         alias member = __traits(getMember, ret, m);
138         member = to!(typeof(member))(each[i]);
139     }}
140     return ret;
141 }
142 
143 
144 bool toBool(string str) pure nothrow @safe @nogc {return str == "true";}
145 
146 ///Use that for making toStruct easier
147 string toString(string str) pure nothrow @safe @nogc {return str;}
148 
149 
150 string toString(bool b) pure nothrow @safe @nogc
151 {
152     return b ? "true" : "false";
153 }
154 
155 TO to(TO, FROM)(FROM f) pure nothrow
156 {
157     static if(is(TO == string))
158     {
159         static if(is(FROM == const(char)*) || is(FROM == char*))
160             return fromStringz(f);
161         else static if(is(FROM == enum))
162             return toString!FROM(f);
163         else
164             return toString(f);
165     }
166     else static if(is(TO == int) || is(TO == long))
167     {
168         static if(!is(FROM == string))
169             return toInt(f.toString);
170         else
171             return cast(TO)toInt(f);
172     }
173     else static if(is(TO == uint) || is(TO == size_t) || is(TO == ubyte) || is(TO == ushort))
174     {
175         static if(!is(FROM == string))
176             return cast(TO)toInt(f.toString);
177         else
178             return cast(TO)toInt(f);
179     }
180     else static if(is(TO == float) || is(TO == double))
181     {
182         static if(!is(FROM == string))
183             return toFloat(f.toString);
184         else
185             return toFloat(f);
186     }
187     else static if(is(TO == bool))
188     {
189         static if(!is(FROM == string))
190             return toBool(f.toString);
191         else
192             return toBool(f);
193     }
194     else
195     {
196         static if(!is(FROM == string))
197             return toStruct!TO(f.toString);
198         else
199             return toStruct!TO(f);
200     }
201 }
202 
203 /// This function can be called at compilation time without bringing runtime
204 string toString(long x) pure nothrow @safe
205 {
206     enum numbers = "0123456789";
207     bool isNegative = x < 0;
208     if(isNegative)
209         x*= -1;
210     size_t div = 10;
211     int length = 1;
212     int count = 1;
213     while(div <= x)
214     {
215         div*=10;
216         length++;
217     }
218     if(isNegative) length++;
219     char[] ret = new char[](length);
220     if(isNegative)
221         ret[0] = '-';
222     div = 10;
223     while(div <= x)
224     {
225         count++;
226         ret[length-count]=numbers[cast(size_t)((x/div)%10)];
227         div*=10;
228     }
229     ret[length-1] = numbers[cast(size_t)(x%10)];
230     return ret[0..$];
231 }
232 
233 
234 string toString(float f) pure nothrow @safe 
235 {
236     if(f != f) return "nan";
237     else if(f == -float.infinity) return "-inf";
238     else if(f == float.infinity) return "inf";
239 
240     bool isNegative = f < 0;
241     if(isNegative)
242         f = -f;
243     
244     float decimal = f - cast(int)f;
245     string ret = (cast(int)f).toString;
246     if(isNegative)
247         ret = "-"~ret;
248     
249     if(decimal == 0)
250         return ret;
251     ret~= '.';
252     long multiplier = 10;
253     while(cast(long)(decimal*multiplier) < (decimal*multiplier))
254     {
255         if(cast(long)(decimal*multiplier) == 0)
256         	ret~= '0';
257         multiplier*=10;
258     }
259     return ret ~ (cast(long)(decimal*multiplier)).toString;
260 }
261 
262 pure string toString(void* ptr) @safe nothrow
263 {
264     return ptr is null ? "null" : toHex(cast(size_t)ptr);
265 }
266 
267 
268 pure string toHex(size_t n) @safe nothrow
269 {
270     enum numbers = "0123456789ABCDEF";
271     int preAllocSize = 1;
272     ulong div = 16;
273     while(div <= n)
274     {
275         div*= 16;
276         preAllocSize++;
277     }
278     div/= 16;
279     char[] ret = new char[](preAllocSize);
280     int i = 0;
281 
282     while(div >= 16)
283     {
284         ret[i++] = numbers[(n/div)%16];
285         div/= 16;
286     }
287     ret[i] = numbers[n%16];
288     return ret[0..$];
289 }
290 
291 
292 string fromUTF16(wstring str) pure nothrow
293 {
294     string ret;
295     foreach(c;str) ret~= c;
296     return ret;
297 }
298 
299 long toInt(string str) pure nothrow @safe @nogc
300 {
301     if(str.length == 0) return 0;
302     str = str.trim;
303 
304     ptrdiff_t i = (cast(ptrdiff_t)str.length)-1;
305 
306     long last = 0;
307     long multiplier = 1;
308     long ret = 0;
309     if(str[0] == '-')
310     {
311         last++;
312         multiplier*= -1;
313     }
314     for(; i >= last; i--)
315     {
316         if(str[i] >= '0' && str[i] <= '9')
317             ret+= (str[i] - '0') * multiplier;
318         else
319             return ret;
320         multiplier*= 10;
321     }
322     return ret;
323 }
324 
325 
326 double toFloat(string str) pure nothrow @safe @nogc
327 {
328     if(str.length == 0) return 0;
329     str = str.trim;
330     if(str == "nan" || str == "NaN") return double.init;
331     if(str == "inf" || str == "infinity" || str == "Infinity") return double.infinity;
332 
333     int integerPart = 0;
334     int decimalPart = 0;
335     int i = 0;    
336     bool isNegative = str[0] == '-';
337     if(isNegative)
338         str = str[1..$];
339     bool isDecimal = false;
340     for(; i < str.length; i++)
341     {
342         if(str[i] =='.')
343         {
344             isDecimal = true;
345             continue;
346         }
347         if(isDecimal)
348             decimalPart++;
349         else
350             integerPart++;
351     }
352     
353     if(decimalPart == 0)
354         return (isNegative ? -1 : 1) * cast(float)str.toInt;
355 
356     i = 0;
357     double decimal= 0;
358     double integer  = 0;
359     long integerMultiplier = 1;
360     double floatMultiplier = 1.0f/10.0f;
361 
362     while(integerPart > 0)
363     {
364         //Iterate the number from backwards towards the greatest value
365         integer+= (str[integerPart - 1] - '0') * integerMultiplier;
366         integerMultiplier*= 10;
367         integerPart--;
368         i++;
369     }
370     i++; //Jump the .
371     while(decimalPart > 0)
372     {
373         decimal+= (str[i] - '0') * floatMultiplier;
374         floatMultiplier/= 10;
375         decimalPart--;
376         i++;
377     }
378     return (integer + decimal) * (isNegative ? -1 : 1);
379 }
380 
381 
382 unittest
383 {
384     assert(toString(500) == "500");
385     assert(toFloat("50.5") == 50.5);
386     assert(toString(100.0) == "100");
387     assert(toInt("-500") == -500);
388     assert(toString("Hello") == "Hello");
389     assert(toString(true) == "true");
390     assert(toString(50.25)== "50.25");
391     assert(toString(0.003) == "0.003");
392     assert(toString(0.999) == "0.999");
393 }